--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 5e8c627e72770f17e4741b53b7c31f645dd51c2d
Parents : f2cf9a8
Author : Mark Qvist <bc7291552be7a58f361522990465165c>
Signature : T66BB85Valid, signed by author
Date : 2026-07-17T13:06:37+02:00
Added keyboard shortcuts for going directly to object details
Changes
3 files changed, 32 insertions(+), 22 deletions(-)
Diff
diff --git a/sbapp/main.py b/sbapp/main.py
index 0596c632..4a99f12a 100644
--- a/sbapp/main.py
+++ b/sbapp/main.py
@@ -1781,7 +1781,8 @@ class SidebandApp(MDApp):
if len(modifiers) > 0 and "ctrl" in modifiers:
if keycode < 40 and keycode > 29:
c_index = keycode-29
- self.conversation_index_action(c_index)
+ if "shift" in modifiers: self.object_index_action(c_index)
+ else: self.conversation_index_action(c_index)
if text == "i": self.incoming_messages_action()
if text == "s": self.connectivity_status(None)
@@ -2106,12 +2107,20 @@ class SidebandApp(MDApp):
def conversation_index_action(self, index):
if self.conversations_view != None and self.conversations_view.list != None:
- i = index-1
- c = self.conversations_view.list.children[0].children
+ i = index-1; c = self.conversations_view.list.children[0].children
if len(c) > i:
item = c[(len(c)-1)-i]
self.conversation_action(item)
+ def object_index_action(self, index):
+ RNS.log(f"OBJ INDEX {index}")
+ if self.conversations_view != None and self.conversations_view.list != None:
+ i = index-1; c = self.conversations_view.list.children[0].children
+ if len(c) > i:
+ item = c[(len(c)-1)-i]
+ if self.include_objects and not self.include_conversations: self.object_details_action(item, source_dest=item.sb_uid, from_objects=True)
+ else: self.object_details_action(item, source_dest=item.sb_uid, from_convs=True)
+
def init_confirm_call_dialog(self, call_dialog_text):
yes_button = MDRectangleFlatButton(text="Call",font_size=dp(18), theme_text_color="Custom", line_color=self.color_accept, text_color=self.color_accept)
no_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18))
@@ -5180,33 +5189,30 @@ class SidebandApp(MDApp):
### Object details screen
######################################
- def object_details_action(self, sender=None, from_conv=False, from_objects=False, from_telemetry=False, source_dest=None, direction="left"):
+ def object_details_action(self, sender=None, from_conv=False, from_convs=False, from_objects=False, from_telemetry=False, source_dest=None, direction="left"):
if self.root.ids.screen_manager.has_screen("object_details_screen"):
- self.object_details_open(sender=sender, from_conv=from_conv, from_objects=from_objects, from_telemetry=from_telemetry, source_dest=source_dest, direction=direction)
+ self.object_details_open(sender=sender, from_conv=from_conv, from_convs=from_convs, from_objects=from_objects, from_telemetry=from_telemetry, source_dest=source_dest, direction=direction)
else:
self.loader_action(direction=direction)
def final(dt):
self.object_details_init()
def o(dt):
- self.object_details_open(sender=sender, from_conv=from_conv, from_objects=from_objects, from_telemetry=from_telemetry, source_dest=source_dest, no_transition=True)
+ self.object_details_open(sender=sender, from_conv=from_conv, from_convs=from_convs, from_objects=from_objects, from_telemetry=from_telemetry, source_dest=source_dest, no_transition=True)
Clock.schedule_once(o, ll_ot)
Clock.schedule_once(final, ll_ft)
def object_details_init(self):
self.object_details_screen = ObjectDetails(self)
- def object_details_open(self, sender=None, from_conv=False, from_objects=False, from_telemetry=False, source_dest=None, direction="left", no_transition=False):
- if no_transition:
- self.root.ids.screen_manager.transition = self.no_transition
+ def object_details_open(self, sender=None, from_conv=False, from_convs=False, from_objects=False, from_telemetry=False, source_dest=None, direction="left", no_transition=False):
+ if no_transition: self.root.ids.screen_manager.transition = self.no_transition
else:
self.root.ids.screen_manager.transition = self.slide_transition
self.root.ids.screen_manager.transition.direction = direction
- if self.sideband.config["telemetry_enabled"] == True:
- self.sideband.update_telemetry()
+ if self.sideband.config["telemetry_enabled"] == True: self.sideband.update_telemetry()
- if source_dest != None:
- telemetry_source = source_dest
+ if source_dest != None: telemetry_source = source_dest
else:
if sender != None and hasattr(sender, "source_dest") and sender.source_dest != None:
telemetry_source = sender.source_dest
@@ -5216,13 +5222,11 @@ class SidebandApp(MDApp):
self.root.ids.nav_drawer.set_state("closed")
if telemetry_source == None:
- if self.include_objects and not self.include_conversations:
- self.objects_action(direction="right")
- else:
- self.conversations_action(direction="right")
+ if self.include_objects and not self.include_conversations: self.objects_action(direction="right")
+ else: self.conversations_action(direction="right")
else:
- Clock.schedule_once(lambda dt: self.object_details_screen.set_source(telemetry_source, from_conv=from_conv, from_objects=from_objects, from_telemetry=from_telemetry), 0.0)
+ Clock.schedule_once(lambda dt: self.object_details_screen.set_source(telemetry_source, from_conv=from_conv, from_convs=from_convs, from_objects=from_objects, from_telemetry=from_telemetry), 0.0)
def vj(dt):
self.root.ids.screen_manager.current = "object_details_screen"
diff --git a/sbapp/ui/guide.py b/sbapp/ui/guide.py
index c5a10d0c..e65ac2be 100644
--- a/sbapp/ui/guide.py
+++ b/sbapp/ui/guide.py
@@ -107,6 +107,7 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti
[b]Navigation[/b]
- [b]Ctrl-[i]n[/i][/b] Go to conversation number [i]n[/i]
+ - [b]Ctrl-Shift-[i]n[/i][/b] Go to object details number [i]n[/i]
- [b]Ctrl-R[/b] Go to Conversations
- [b]Ctrl-O[/b] Go to Objects & Devices
- [b]Ctrl-E[/b] Go to Voice
diff --git a/sbapp/ui/objectdetails.py b/sbapp/ui/objectdetails.py
index 910a0695..5ff6f154 100644
--- a/sbapp/ui/objectdetails.py
+++ b/sbapp/ui/objectdetails.py
@@ -45,6 +45,7 @@ class ObjectDetails():
self.from_telemetry = False
self.from_conv = False
self.from_objects = False
+ self.from_convs = False
self.viewing_self = False
self.viewing_interface = False
self.interface_lat = None
@@ -97,6 +98,7 @@ class ObjectDetails():
if self.from_telemetry: self.app.telemetry_action(direction="right")
else:
if self.from_conv: self.app.open_conversation(self.object_hash, direction="right")
+ elif self.from_convs: self.app.conversations_action(direction="right")
elif self.from_objects: self.app.objects_action(direction="right")
else: self.app.close_sub_map_action()
@@ -127,7 +129,7 @@ class ObjectDetails():
def reload_telemetry(self, sender=None, notoast=False):
if self.object_hash != None:
- self.set_source(self.object_hash, from_conv=self.from_conv, from_objects=self.from_objects, from_telemetry=self.from_telemetry)
+ self.set_source(self.object_hash, from_conv=self.from_conv, from_convs=self.from_convs, from_objects=self.from_objects, from_telemetry=self.from_telemetry)
if not notoast: toast("Reloaded telemetry for object")
def use_interface_action(self, sender=None):
@@ -200,7 +202,7 @@ class ObjectDetails():
telemetry = [[ts, t.packed()]]
return telemetry, iface["name"], self.app.iface_appearance(iface["type"]), iface
- def set_source(self, source_dest, from_conv=False, from_objects=False, from_telemetry=False, prefetched=None):
+ def set_source(self, source_dest, from_conv=False, from_convs=False, from_objects=False, from_telemetry=False, prefetched=None):
try:
self.object_hash = source_dest
own_address = self.app.sideband.lxmf_destination.hash
@@ -233,6 +235,8 @@ class ObjectDetails():
self.from_conv = False
if from_objects: self.from_objects = True
else: self.from_objects = False
+ if from_convs: self.from_convs = True
+ else: self.from_convs = False
if self.viewing_interface:
if self.telemetry_request_buttons in self.screen.ids.details_screen_container.children:
@@ -903,11 +907,12 @@ class RVDetails(MDRecycleView):
rxb = d["traffic_rxb"]; txb = d["traffic_txb"]
rxs = d["speed_rx"]; txs = d["speed_tx"]
rxsi = d["speed_rx_inst"]; txsi = d["speed_tx_inst"]
- pthc = len(d["path_table"])
+ pthc = d["path_count"]
+ mem_str = f", using [b]{RNS.prettysize(mem)}[/b] RAM" if mem else ""
formatted_values = f"Reticulum Transport [b]{te}[/b] with [b]{ifc}[/b] interface{'s' if ifc != 1 else ''}"
extra_entries.append({"icon": "routes", "text": f"[b]{pthc}[/b] destination{'s' if pthc != 1 else ''} reachable, [b]{lnkc}[/b] active link{'s' if lnkc != 1 else ''}"})
- extra_entries.append({"icon": "clock-digital", "text": f"Up for [b]{RNS.prettytime(upt, compact=True)}[/b], using [b]{RNS.prettysize(mem)}[/b] RAM"})
+ if upt: extra_entries.append({"icon": "clock-digital", "text": f"Up for [b]{RNS.prettytime(upt, compact=True)}[/b]{mem_str}"})
extra_entries.append({"icon": "speedometer", "text": f"Throughput [b]{RNS.prettyspeed(rxs)}[/b] in, [b]{RNS.prettyspeed(txs)}[/b] out"})
extra_entries.append({"icon": "cube-send", "text": f"Traffic [b]{RNS.prettysize(rxb)}[/b] in, [b]{RNS.prettysize(txb)}[/b] out"})
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────